﻿using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YmtAuth.Common.Utility;

namespace YmtAuth.Domain
{
    public abstract class Entity<TKey> : IEntity<TKey> 
        //where TKey : IComparable<TKey>, IEquatable<TKey>
    {
        private int? entityHashCode;

        //[BsonRepresentation(BsonType.ObjectId)]
        [BsonId]
        public virtual TKey Id { get; set; }

        public Entity()                  
        {

        }

        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is Entity<TKey>))
                return false;

            if (Object.ReferenceEquals(this, obj))
                return true;
            Entity<TKey> item = (Entity<TKey>)obj;

            return item.CompareValues().SequenceEqual(this.CompareValues());
        }
        public override int GetHashCode()
        {
            if (entityHashCode.HasValue)
                return entityHashCode.Value;

            entityHashCode = HashCodeHelper.CombineHashCodes(CompareValues());
            return entityHashCode.Value;
        }
        public static bool operator ==(Entity<TKey> left, Entity<TKey> right)
        {
            if (Object.Equals(left, null))
                return (Object.Equals(right, null)) ? true : false;
            else
                return left.Equals(right);
        }

        public static bool operator !=(Entity<TKey> left, Entity<TKey> right)
        {
            return !(left == right);
        }
        protected abstract IEnumerable<object> CompareValues();
    }

    [Obsolete("使用Entity<>")]
    public abstract class Entity : IEntity
    {
        public virtual object _id { get; protected set; }
        [BsonIgnore]
        public virtual string DbName { get; protected set; }
        [BsonIgnore]
        public virtual string TbName { get; protected set; }

        private int? _requestedHashCode;
        protected virtual IEnumerable<object> CompareValues()
        {
            yield return _id;
        }
        public Entity()
        {
            _id = string.Empty;//Guid.NewGuid().ToString("N");
        }
        public virtual bool IsEmpty()
        {
            return string.IsNullOrEmpty(_id.ToString());
        }

        /// <summary>
        /// <see cref="M:System.Object.Equals"/>
        /// </summary >
        /// <param name="obj"><see cref="M:System.Object.Equals"/></param>
        /// <returns><see cref="M:System.Object.Equals"/></returns>
        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is Entity))
                return false;

            if (Object.ReferenceEquals(this, obj))
                return true;

            Entity item = (Entity)obj;

            if (string.IsNullOrEmpty(item._id.ToString()) || string.IsNullOrEmpty(this._id.ToString()))
                return false;
            else
            {
                if (!CompareValues().Any())
                    return item._id == this._id;
                else
                    return item.CompareValues().SequenceEqual(this.CompareValues());
            }
        }

        /// <summary>
        /// <see cref="M:System.Object.GetHashCode"/>
        /// </summary>
        /// <returns><see cref="M:System.Object.GetHashCode"/></returns>
        public override int GetHashCode()
        {
            if (!IsEmpty())
            {
                if (!_requestedHashCode.HasValue)
                    _requestedHashCode = HashCodeHelper.CombineHashCodes(CompareValues());// this._id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)

                return _requestedHashCode.Value;
            }
            else
                return base.GetHashCode();

        }

        public static bool operator ==(Entity left, Entity right)
        {
            if (Object.Equals(left, null))
                return (Object.Equals(right, null)) ? true : false;
            else
                return left.Equals(right);
        }

        public static bool operator !=(Entity left, Entity right)
        {
            return !(left == right);
        }

        protected virtual void SetTbMapping()
        {
            this.DbName = "Ymt_Login_Auth_201402";//Ymt_Auth_System
            this.TbName = this.GetType().Name;
        }
    }
}
